package statistics;

import java.util.*;

/**
 * This is an abstract class, called DiscreteDistribution. It represents
 * a discrete distribution.
 */

public abstract class DiscreteDistribution extends Distribution {

  /**
   * Returns P(X = k), X is a random variable.
   * @param k
   */

   public abstract double probabilityFunction(int k);

  /**
   * Returns P(X &le; k), X is a random variable.
   * @param k
   */

   public double cumulativeProbabilityFunction(int k) {
     double p = 0.0;
     for (int i = 0; i < k; i++) p += probabilityFunction(i);
     return p;
   }
   
   /**
    * Fits a discrete distribution to the specified mean and standard deviation.
    * @param mean the mean
    * @param deviation the standard deviation
    * @return the fit DiscreteDistribution.
    */
    
   public static DiscreteDistribution fit(double mean, double deviation) {
     return fit(mean, deviation, new Random());
   }

   /**
    * Fits a discrete distribution to the specified mean and standard deviation.
    * @param mean the mean
    * @param deviation the standard deviation
    * @param random the random number generator
    * @return the fit DiscreteDistribution.
    */

   public static DiscreteDistribution fit(double mean, double deviation, Random random) {
     DiscreteDistribution fitDist = null;
     double variationCoefficient = deviation / mean;
     double a = 0.0;
     if (mean != 0) a = variationCoefficient*variationCoefficient - 1.0/mean;

     /* check whether discrete fit is possible */
     double c2 = variationCoefficient*variationCoefficient;
     double EX = mean;
     int k;
     for (k = 0; k+1 <= EX; k ++);
     if (c2 < (2.0 * k + 1.0) / EX - (k * (k + 1.0)) / (EX * EX) - 1.0) {
       System.err.println("No discrete fit possible. Results may be inaccurate.");
     }

     if(a == 0) {
       fitDist = new PoissonDistribution(mean,random);
     }
     else if(a >= 1) {
       fitDist = new MixGeometricDistribution(mean,a,random);
     }
     else if(a < 0) {
       fitDist = new MixBinomialDistribution(mean,a,random);
     }
     else  { // (0 < a < 1)
       fitDist = new MixNegBinomialDistribution(mean,a,random);
     }
     return fitDist;
   }
}
